package com.abysscore.yachtdesign;

import java.util.Vector;

/*
 * {---------------------------------------------------------------------------------------------------}
     {                                       TFasterList                                                 }
     {                                                                                                   }
     {   TFasterlist is an improved version of Delphi's standard TList object.                           }
     {   It is stripped of unneccesary code to improve speed.                                            }
     {   Additionally the contents (pointers) can be sorted so that the time needed to search an item    }
     {   has been redced with a factor 2^n                                                               }
     {                                                                                                   }
     {   Use the SORT            method to sort the list                                                 }
     {   Use the SORTEDINDEXOF() function to perform a binary search in the sorted list                  }
     {---------------------------------------------------------------------------------------------------}
 */
public class TFasterList {
    private int FCount;
    private int FCapacity;
    private boolean FSorted;
    private boolean FUseUserData;
    private Vector FList;
    private Vector FData; // Pointers to any user specified object
    
    private Object FGet(int Index)
    {
    	Object Result;
    	if (Index>=0 && Index<FCount)
   	   	{
    		Result = FList.get(Index);
   	   	}
   	   	else
   	   		Result = null;
   	   
   	   	return Result;
    }
    
    private Object FGetObject(int Index)
    {
    	Object Result;
    	if (Index>=0 && Index<FCount && FUseUserData)
    	{
    		Result = FData.get(Index);
    	}
    	else 
    		Result=null;
    	return Result;
    }
    
    private int FGetMemory()
    {
    	int Result;

    	Result = 4+           // self        : pointer
                 4+           // FCapacity   : int
                 4+           // fcount      : int
                 1+           // FSorted     : boolean
                 1+           // FUseUserdata: boolean
                 4*FCapacity;
       if (FUseUserData)
       {
    	   Result+=4*FCapacity;
       }
       return Result;
    }
    
    private void FGrow()
    {
    	int Delta;

    	if (FCapacity>64)
    	{
    		Delta = FCapacity / 4;
    	    if (Delta>1000) 
    	    	Delta = 1000;    
    	}
    	else
    		if (FCapacity>8)
    			Delta = 16; 
    		else
    			Delta = 4;
    	  FSetCapacity(FCapacity + Delta);
    }
    
    private void FSet(int Index, Object Item)
    {
    	FList.add(Index, Item);
    	if (FUseUserData)
    		FData.add(Index, null);
    }
    private void FSetObject(int Index, Object Pointer)
    {
    	if(!FUseUserData)
    	{
    		FData.setSize(FCapacity);
    	    FUseUserData = true;
    	}
    	FData.add(Index, Pointer);
    }
    private void FSetCapacity(int NewCapacity)
    {
    	FList.setSize(NewCapacity);
    	if(FUseUserData)
    		FData.setSize(NewCapacity);
    	FCapacity = NewCapacity;
    	if(FCapacity<FCount)
    		FCount = FCapacity;
    }
    
    public void Add(Object Item)
    {
    	//var Cur,Prev:Cardinal;
    	
    	if(FCount==FCapacity)
    		FGrow();
    	FList.add(FCount, Item);
    	if(FUseUserData)
    		FData.add(FCount, null);
    	if (FCount<=1)
    		FSorted = true;
    	else if (FSorted)
    	{
    		//TODO check what the heck this is supposed to do
    	      //Prev:=Cardinal(FList[FCount-1]);
    	      //Cur:=Cardinal(Item);
    	      //if (Prev < Cur) FSorted = true;
    	      //else FSorted = false; 
    	} 
    	FCount++;
    }
    public void AddList(TFasterList List)
    {
    	int NewCap;
    	NewCap = FCount+List.FCount;
    	FUseUserData = FUseUserData || List.FUseUserData;
    	if (NewCap>FCapacity) 
    	{
    	      FCapacity = NewCap;
    	      FList.setSize(FCapacity);
    	      if (FUseUserData)
    	    	  FData.setSize(FCapacity);
    	}
    	while (!List.FList.isEmpty())
    		FList.add(List.FList.remove(0));

    	if (FUseUserData)
        	while (!List.FList.isEmpty())
        		FData.add(List.FData.remove(0));

    	FSorted = false;
    	FCount++;
    	List.FCount++;
    }
    public void AddObject(Object Item, Object UserObject)
    {
    	//var Cur,Prev:Cardinal;
    	
    	if (FCount==FCapacity) FGrow();
    	if (!FUseUserData)
    	{
    		FData.setSize(FCapacity);
    		FUseUserData = true;
    	}

    	FList.add(FCount,Item);
    	FData.add(FCount,UserObject);
    	if (FCount<=1)
    		FSorted = true;
    	else if(FSorted)
    	{
    		//TODO same as in above, what this should do?!
    		//Prev:=Cardinal(FList[FCount-1]);
    	    //Cur:=Cardinal(Item);
    	    //FSorted:=Prev<Cur;
    	}
    	FCount++;
    }
    public void AddSorted(Object Item)
    {
    	long Address;
    	int L,H,Mid;
    	
    	if(FCount == FCapacity) FGrow();
    	if (FCount==0) 
    	{ 
    	      FList.add(Item);
    	      if (FUseUserData) FData.add(FCount,null);
    	      FCount++;
    	}
    	else
    	{
    	      Address = Item.hashCode();
    	      // check start
    	      if (Address < FList.get(0).hashCode()) 
    	      {
    	    	  // insert at start
    	    	  FList.add(0, Item);
    	    	  if (FUseUserData)
    	    	  {
    	            
    	            FData.add(0, null);
    	         }
    	      }
    	      else if (Address > FList.get(FCount-1).hashCode()) 
    	      {
    	         // add at end
    	         FList.add(Item);
    	         if (FUseUserData) FData.add(FCount,null);
    	      } 
    	      else
    	      {
    	         // perform binary search to quickly find the location
    	         L = 0;
    	         H = FCount-1;
    	         while(H-L>1)
    	         {
    	            Mid = (int)(0.5*(L+H));
    	            if (Address < FList.get(Mid).hashCode()) 
    	            		H = Mid-1;
    	            else L = Mid+1;
    	         }
    	         if (Address < FList.get(L).hashCode()) 
    	        		 Mid = L;
    	         else if (Address < FList.get(H).hashCode())
    	        	 Mid = H; 
    	         else
    	        	 Mid = H+1;
    	         //Move(FList[Mid],FList[Mid+1],(FCount-Mid)*4);
    	         FList.add(Mid,Item);
    	         if (FUseUserData)
    	         {
    	            //Move(FData[Mid],FData[Mid+1],(FCount-Mid)*4);
    	            FData.add(Mid,null);
    	         }
    	      }
    	      FCount++;
    	}
    }
    public void AddSortedObject(Object Item, Object UserObject)
    {
    	
    	long Address;
    	int L,H,Mid;

    	if (!FUseUserData)
    	{
    		FUseUserData = true;
    		FData.setSize(FCapacity);
    	}
    	if (FCount==FCapacity) FGrow();
    	if (FCount==0)
    	{ 
    		FList.add(FCount,Item);
    		if (FUseUserData)
    			FData.add(FCount,UserObject);
    		FCount++;
    	}
    	else
    	{
    	      Address = Item.hashCode();
    	      // check start
    		if (Address < FList.get(0).hashCode())
    		{
    	         // insert at start
    	         FList.add(0,Item);
    	         if (FUseUserData)
    	         {
    	            FData.add(0,UserObject);
    	         }
    		}
    		else if (Address > FList.get(FCount-1).hashCode())
    		{
    	         // add at end
    	         FList.add(Item);
    	         if (FUseUserData)
    	        	 FData.add(UserObject);
    		}
    		else
    		{
    	         // perform binary search to quickly find the location
    	         L = 0;
    	         H = FCount-1;
    	         while (H-L>1)
    	         {
    	            Mid =(int)(0.5*(L+H));
    	            if (Address < FList.get(Mid).hashCode())  H = Mid-1;
                    else L = Mid+1;
    	         }
    	         if (Address < FList.get(L).hashCode()) 
    	        	 Mid = L;
    	         else if (Address < FList.get(H).hashCode()) 
    	        	 Mid = H; 
    	         else
    	        	 Mid = H+1;
    	         //Move(FList[Mid],FList[Mid+1],(FCount-Mid)*4);
    	         FList.add(Mid,Item);
    	         if (FUseUserData)
    	         {
    	            //Move(FData[Mid],FData[Mid+1],(FCount-Mid)*4);
    	            FData.add(Mid, UserObject);
    	         }
    		}
    	    FCount++;
    	}
    }
    public void Assign(TFasterList List)
    {
    	FUseUserData = List.FUseUserData;
    	Capacity(List.Count());
    	/*Move(List.FList[0],FList[0],List.Count*SizeOf(Pointer));
    	if (FUseUserData)
    		Move(List.FData[0],FData[0],List.Count*SizeOf(Pointer));*/
    	FList = List.FList;
    	FData = List.FData;
    	FCount = List.Count();
    	FSorted = List.FSorted;
    }
    
    public TFasterList()
    {
    	FCount=0;
    	FCapacity=0;
    	FSorted=false;
    	FList = new Vector();
    	FData = new Vector();
    }
    public void Clear()
    {
    	FCount=0;
    	FSetCapacity(0);
    	FSorted=false;
    	FUseUserData=false;
    }
    //public destructor Destroy;                                                     override;
    public void Delete(int Index)
    {
    	FCount--;
    	if (Index<FCount)
    	{
    		//Move(FList[Index+1],FList[Index],(FCount-Index)*SizeOf(Pointer));
    		FList.remove(Index);
    		if (FUseUserData)
    			//Move(FData[Index+1],FData[Index],(FCount-Index)*SizeOf(Pointer));
    			FData.remove(Index);
    	}
    }
    public void Exchange(int Index1, int Index2)
    {
    	Object Item;

    	Item=FList.remove(Index1);
     	FList.add(Index1, FList.remove(Index2));
    	FList.add(Index2, Item);
    	if (FUseUserData)
    	{
    		Item = FData.remove(Index1);
    		FData.add(Index1, FData.remove(Index2));
    		FData.add(Index2, Item);
    	}
    	if (FSorted)
    		FSorted=false;
    }
    public int IndexOf(Object Item)
    {
    	int I;
    	int Result = -1;
    	for (I=1; I < FCount; I++)
    		if (FList.get(I-1)==Item)
    		{
    	      Result = I-1;
    	      break;
    		}
    	return Result;
    }
    public void Insert(int Index, Object Item)
    {
    	if (FCount==FCapacity) FGrow();
    	if (Index < FCount)
    	{
    		FList.add(Index, Item);
    		if (FUseUserData)
    			FData.add(Index, null);
    		if (FSorted) FSorted=false;
    		FCount++;
    	}
    }
    private void Swap(int I, int J)
    {
    	Object Tmp;
    	Tmp=FList.remove(I);
    	FList.add(I,FList.remove(J-1));
    	FList.add(J,Tmp);	
    	if (FUseUserData) 
    	{
    		Tmp=FData.remove(I);
    		FData.add(I,FData.remove(J-1));
    		FData.add(J,Tmp);
    	}
    }

    private void QuickSort(int L, int R)
    {
		long Val;
		int I = L;
		int J = R;
		Val = FList.get((I+J)/2).hashCode();
		while (I<=J)
		{
			while (FList.get(I).hashCode() < Val)
				I++;
			while (Val < FList.get(J).hashCode())
				J--;
			if(I<=J)
			{
				Swap(I,J);
				I++;
				J--;
			}
		}
		if(L<J)
			QuickSort(L,J);
		if(I<R)
			QuickSort(I,R);    				
    	
    }
    
    public void Sort()
    {
    	if (FCount > 1 && !FSorted)
    	{	
    		QuickSort(0,FCount-1);
    		FSorted=true;
    	}
    }
    public int SortedIndexOf(Object Item)
    {
    	int Result = -1;
    	long MemAddr;
    	long MidVal;
    	int	L,H,Mid;

    	MemAddr = Item.hashCode();
    	L = 0;
    	H = FCount-1;
    	while (L<=H)
    	{
    		Mid = (int)(0.5*(L+H));
    		MidVal = FList.get(Mid).hashCode();
    		if (MemAddr==MidVal)
    		{
    			Result = Mid;
    	        break;
    		}
    		else
    		{
    			if (MemAddr<MidVal)
    				H=Mid-1;
    	        else
    	        	L=Mid+1;
    		}
    	}
    	return Result;
    }
    
    
    public int Capacity() { return FCapacity; }
    public void Capacity(int val) { FSetCapacity(val); }
    public int Count() { return FCount; }
    public Object Items(int Index)
    {
    	return FGet(Index);
    }
    public void Items(int Index, Object Item)
    {
    	FSet(Index, Item);
    }

    public int Memory() { return FGetMemory(); }
    public Object Objects(int Index)
    {
    	return FGetObject(Index);
    }
    public void Objects(int Index, Object item)
    {
    	FSetObject(Index, item);
    }
}